﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Threading;

namespace NielsDerdaele.HttpSocket.Windows {
    public class HttpServerClientSocket {
        struct WQueueElement {
            public byte[] buffer;
            public int offset;
            public int count;
        }      
        
        #region Variables
        private bool flushed = false;
        private ManualResetEvent readEvent = new ManualResetEvent(false);              
        private Queue<HttpListenerContext> callbacks = new Queue<HttpListenerContext>();
        private Dictionary<int, byte[]> readDictionary = new Dictionary<int, byte[]>();
        private List<byte> writeData = new List<byte>();
        private int id = 0, currentReadId = 0;
        #endregion

        public string UniqueId { get; private set; }        

        public HttpServerClientSocket(string uniqueId) {
            UniqueId = uniqueId;
        }

        public void AddCallback(HttpListenerContext context) {            
            if (flushed) {
                sendData(context);
            } else {
                lock (callbacks) {
                    callbacks.Enqueue(context);
                }
            }
        }        
        public void AddData(int id, byte[] buffer) {
            lock (readDictionary) {
                readDictionary.Add(id, buffer);
                readEvent.Set();                
            }
        }

        public void Flush() {
            HttpListenerContext callback = null;            
            lock (callbacks) {
                if (callbacks.Count > 0) {
                    callback = callbacks.Dequeue(); //haal een callback uit de queue
                } else {                    
                    flushed = true;
                    return;
                }
            }
            sendData(callback);
        }                
        public void Write(byte[] buffer, int offset, int count) {
            lock (writeData) { //voeg toe aan een buffer!
                writeData.AddRange(buffer.Skip(offset).Take(count));
            }            
        }
        private void sendData(HttpListenerContext callback) {           
            byte[] buffer;
            lock (writeData) {                
                //TODO: id toevoegen
                
                buffer = writeData.ToArray();
                writeData.Clear();
            }
            callback.Response.ContentType = "binary/octet-stream";
            callback.Response.ContentLength64 = buffer.Length;
            callback.Response.Headers.Add(HttpResponseHeader.CacheControl, "no-cache");            
            callback.Response.OutputStream.Write(buffer, 0, buffer.Length);            
            callback.Response.Close();
            flushed = false;
        }

        public int Read(byte[] buffer, int offset, int count) {
            bool containsReadId = false;
            
            do {
                lock (readDictionary) { //neem een lock op de dictionary die de binnengekomen stukken bevat
                    containsReadId = readDictionary.ContainsKey(currentReadId); //controleer als het stuk waarin we geinteresseerd zijn aanwezig is
                    if (!containsReadId) readEvent.Reset(); //Reset semaphore                    
                }
                if (!containsReadId) readEvent.WaitOne();
            } while (!containsReadId);


            //Stuk is aanwezig
            byte[] data;
            lock (readDictionary) {
                data = readDictionary[currentReadId];//lees dit stuk in
            }
            

            if (data.Length > count) { //ons blok is groter dan wat we wensen te lezen, we kunnen dus count elementen kopieren
                Array.Copy(data, 0, buffer, offset, count); //schrijf naar buffer
                var temp = new byte[data.Length - count];
                Array.Copy(data, count, temp, 0, temp.Length);
                lock (readDictionary) {
                    readDictionary[currentReadId] = temp;
                }
                return count;
            } else {
                Array.Copy(data, 0, buffer, offset, data.Length); //schrijf naar buffer en verwijder uit dict
                lock (readDictionary) {
                    readDictionary.Remove(currentReadId);
                    currentReadId++;
                }
                return data.Length;
            }
        }
    }
}
